1   /*
2    * Copyright (C) 2013 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.collect;
16  
17  import com.google.common.annotations.GwtCompatible;
18  
19  import java.util.AbstractCollection;
20  import java.util.AbstractSet;
21  import java.util.Collection;
22  import java.util.Iterator;
23  import java.util.Map;
24  import java.util.Set;
25  
26  import javax.annotation.Nullable;
27  
28  /**
29   * Skeletal, implementation-agnostic implementation of the {@link Table} interface.
30   * 
31   * @author Louis Wasserman
32   */
33  @GwtCompatible
34  abstract class AbstractTable<R, C, V> implements Table<R, C, V> {
35  
36    @Override
37    public boolean containsRow(@Nullable Object rowKey) {
38      return Maps.safeContainsKey(rowMap(), rowKey);
39    }
40  
41    @Override
42    public boolean containsColumn(@Nullable Object columnKey) {
43      return Maps.safeContainsKey(columnMap(), columnKey);
44    }
45  
46    @Override
47    public Set<R> rowKeySet() {
48      return rowMap().keySet();
49    }
50  
51    @Override
52    public Set<C> columnKeySet() {
53      return columnMap().keySet();
54    }
55  
56    @Override
57    public boolean containsValue(@Nullable Object value) {
58      for (Map<C, V> row : rowMap().values()) {
59        if (row.containsValue(value)) {
60          return true;
61        }
62      }
63      return false;
64    }
65  
66    @Override
67    public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) {
68      Map<C, V> row = Maps.safeGet(rowMap(), rowKey);
69      return row != null && Maps.safeContainsKey(row, columnKey);
70    }
71  
72    @Override
73    public V get(@Nullable Object rowKey, @Nullable Object columnKey) {
74      Map<C, V> row = Maps.safeGet(rowMap(), rowKey);
75      return (row == null) ? null : Maps.safeGet(row, columnKey);
76    }
77  
78    @Override
79    public boolean isEmpty() {
80      return size() == 0;
81    }
82  
83    @Override
84    public void clear() {
85      Iterators.clear(cellSet().iterator());
86    }
87  
88    @Override
89    public V remove(@Nullable Object rowKey, @Nullable Object columnKey) {
90      Map<C, V> row = Maps.safeGet(rowMap(), rowKey);
91      return (row == null) ? null : Maps.safeRemove(row, columnKey);
92    }
93  
94    @Override
95    public V put(R rowKey, C columnKey, V value) {
96      return row(rowKey).put(columnKey, value);
97    }
98  
99    @Override
100   public void putAll(Table<? extends R, ? extends C, ? extends V> table) {
101     for (Table.Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
102       put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
103     }
104   }
105 
106   private transient Set<Cell<R, C, V>> cellSet;
107 
108   @Override
109   public Set<Cell<R, C, V>> cellSet() {
110     Set<Cell<R, C, V>> result = cellSet;
111     return (result == null) ? cellSet = createCellSet() : result;
112   }
113 
114   Set<Cell<R, C, V>> createCellSet() {
115     return new CellSet();
116   }
117 
118   abstract Iterator<Table.Cell<R, C, V>> cellIterator();
119 
120   class CellSet extends AbstractSet<Cell<R, C, V>> {
121     @Override
122     public boolean contains(Object o) {
123       if (o instanceof Cell) {
124         Cell<?, ?, ?> cell = (Cell<?, ?, ?>) o;
125         Map<C, V> row = Maps.safeGet(rowMap(), cell.getRowKey());
126         return row != null && Collections2.safeContains(
127             row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue()));
128       }
129       return false;
130     }
131 
132     @Override
133     public boolean remove(@Nullable Object o) {
134       if (o instanceof Cell) {
135         Cell<?, ?, ?> cell = (Cell<?, ?, ?>) o;
136         Map<C, V> row = Maps.safeGet(rowMap(), cell.getRowKey());
137         return row != null && Collections2.safeRemove(
138             row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue()));
139       }
140       return false;
141     }
142 
143     @Override
144     public void clear() {
145       AbstractTable.this.clear();
146     }
147 
148     @Override
149     public Iterator<Table.Cell<R, C, V>> iterator() {
150       return cellIterator();
151     }
152 
153     @Override
154     public int size() {
155       return AbstractTable.this.size();
156     }
157   }
158 
159   private transient Collection<V> values;
160 
161   @Override
162   public Collection<V> values() {
163     Collection<V> result = values;
164     return (result == null) ? values = createValues() : result;
165   }
166   
167   Collection<V> createValues() {
168     return new Values();
169   }
170   
171   Iterator<V> valuesIterator() {
172     return new TransformedIterator<Cell<R, C, V>, V>(cellSet().iterator()) {
173       @Override
174       V transform(Cell<R, C, V> cell) {
175         return cell.getValue();
176       }
177     };
178   }
179 
180   class Values extends AbstractCollection<V> {
181     @Override
182     public Iterator<V> iterator() {
183       return valuesIterator();
184     }
185 
186     @Override
187     public boolean contains(Object o) {
188       return containsValue(o);
189     }
190 
191     @Override
192     public void clear() {
193       AbstractTable.this.clear();
194     }
195 
196     @Override
197     public int size() {
198       return AbstractTable.this.size();
199     }
200   }
201 
202   @Override public boolean equals(@Nullable Object obj) {
203     return Tables.equalsImpl(this, obj);
204   }
205 
206   @Override public int hashCode() {
207     return cellSet().hashCode();
208   }
209 
210   /**
211    * Returns the string representation {@code rowMap().toString()}.
212    */
213   @Override public String toString() {
214     return rowMap().toString();
215   }
216 }